﻿using IsmsExplorer.Model.AssetRegister;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using IsmsExplorer.Model.Projects;
using IsmsExplorer.Infrastructure.RepositoryFramework;
using IsmsExplorer.Infrastructure;
using System.Collections.Generic;

namespace IsmsExplorer.UnitTests {
    /// <summary>
    ///This is a test class for IAssetRepositoryTest and is intended
    ///to contain all IAssetRepositoryTest Unit Tests
    ///</summary>
    [TestClass()]
    public class IAssetRepositoryTest {
        private TestContext testContextInstance;
        private const string PROJECT_NAME = "TestProject_1";

        IProjectRepository _projRep;
        IAssetRepository _assetRep;
        IAssetCategoryRepository _categoryRep;
        ILocationRepository _locationRep;
        IRoleRepository _roleRep;

        Project project;
        AssetCategory category1;
        AssetCategory category2;
        Role role1;
        Role role2;
        Location location1;
        Location location2;
        
        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext {
            get {
                return testContextInstance;
            }
            set {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext) {            
        //}

        //// Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup() {
        //}

        //Use TestInitialize to run code before running each test
        [TestInitialize()]
        public void MyTestInitialize() {
            _projRep = RepositoryFactory.GetRepository<IProjectRepository, Project>(new UnitOfWork());
            _assetRep = RepositoryFactory.GetRepository<IAssetRepository, Asset>(new UnitOfWork());
            _categoryRep = RepositoryFactory.GetRepository<IAssetCategoryRepository, AssetCategory>(new UnitOfWork());
            _locationRep = RepositoryFactory.GetRepository<ILocationRepository, Location>(new UnitOfWork());
            _roleRep = RepositoryFactory.GetRepository<IRoleRepository, Role>(new UnitOfWork());

            // Create Project
            project = new Project(PROJECT_NAME);
            _projRep.Add(project);
            _projRep.UnitOfWork.Commit();

            category1 = new AssetCategory("Information");
            category2 = new AssetCategory("Hardware");
            _categoryRep.Add(category1);
            _categoryRep.Add(category2);
            _categoryRep.UnitOfWork.Commit();

            role1 = new Role("IT Systems administrator");
            role2 = new Role("IT Manager");
            _roleRep.Add(role1);
            _roleRep.Add(role2);
            _roleRep.UnitOfWork.Commit();

            location1 = new Location("MRC Centre");
            location2 = new Location("Aimes Centre");
            _locationRep.Add(location1);
            _locationRep.Add(location2);
            _locationRep.UnitOfWork.Commit();            

        }

        //Use TestCleanup to run code after each test has run
        [TestCleanup()]
        public void MyTestCleanup() {
            // Delete objects in database.
            _categoryRep.Remove(category1);
            _categoryRep.Remove(category2);
            _categoryRep.UnitOfWork.Commit();

            _locationRep.Remove(location1);
            _locationRep.Remove(location2);
            _locationRep.UnitOfWork.Commit();

            _roleRep.Remove(role1);
            _roleRep.Remove(role2);
            _roleRep.UnitOfWork.Commit();

            _projRep.Remove(project);
            _projRep.UnitOfWork.Commit();

            _projRep = null;
            _assetRep = null;
            _categoryRep = null;
            _locationRep = null;
            _roleRep = null;
        }

        #endregion


        internal virtual IAssetRepository CreateIAssetRepository() {
            // TODO: Instantiate an appropriate concrete class.
            IAssetRepository target = null;
            return target;
        }

        /// <summary>
        ///A test for FindByControl
        ///</summary>
        [TestMethod()]
        public void FindByTest() {
            Asset asset1 = new Asset(0, "User directory", 5) {
                Project = project,
                Owner = role1,
                Location = location1,
                Category = category1
            };
            Asset asset2 = new Asset(0, "Lead Researcher", 6) {
                Project = project,
                Owner = role2,
                Location = location2,
                Category = category2
            };

            _assetRep.Add(asset1);
            _assetRep.Add(asset2);
            _assetRep.UnitOfWork.Commit();
            Assert.IsTrue(asset1.ID > 0);
            Assert.IsTrue(asset2.ID > 0);
            IList<Asset> assets = _assetRep.FindBy(project);
            Assert.AreEqual(2, assets.Count);

            // Try to insert using the indexer.
            Asset newAsset1 = new Asset(0, "Non Existent Asset 1", 5) {
                Project = project,
                Owner = role2,
                Location = location2,
                Category = category2
            };
            _assetRep[newAsset1.ID] = newAsset1;
            _assetRep.UnitOfWork.Commit();
            Assert.IsNotNull(newAsset1);
            Assert.IsTrue(newAsset1.ID > 0);
            newAsset1 = _assetRep.FindBy(newAsset1.ID);
            Assert.IsNotNull(newAsset1);
            // Now delete it and try and find it - should not be there!
            _assetRep.Remove(newAsset1);
            _assetRep.UnitOfWork.Commit();
            newAsset1 = _assetRep.FindBy(newAsset1.ID);
            Assert.IsNull(newAsset1);

            string assetName = "Non existent Asset";
            Asset asset = _assetRep.FindBy(assetName, project);
            Assert.IsNull(asset);

            assetName = "User directory";
            asset = _assetRep.FindBy(assetName, project);
            Assert.IsNotNull(asset);
            Assert.AreEqual(assetName, asset.Name);
            Assert.AreEqual(asset1.ValueCIA, asset.ValueCIA);
            Assert.AreEqual(asset1.Project.ID, asset.Project.ID);
            Assert.AreEqual(asset1.Owner.ID, asset.Owner.ID);
            Assert.AreEqual(asset1.Location.ID, asset.Location.ID);
            Assert.AreEqual(asset1.Category.ID, asset.Category.ID);

            // Try to update the asset.
            asset.ValueCIA = 3;
            asset.Owner = asset2.Owner;
            asset.Location = asset2.Location;
            asset.Category = asset2.Category;
            _assetRep[asset.ID] = asset;
            _assetRep.UnitOfWork.Commit();

            // Find the asset again by name (and project ID) and see if changes took place.            
            asset = _assetRep.FindBy(assetName, project);
            Assert.IsNotNull(asset);
            Assert.AreEqual(assetName, asset.Name);
            // New ValueCIA.
            Assert.AreEqual(3, asset.ValueCIA);
            // Same project.
            Assert.AreEqual(asset1.Project.ID, asset.Project.ID);
            // New location, owner, category
            Assert.AreEqual(asset2.Owner.ID, asset.Owner.ID);
            Assert.AreEqual(asset2.Location.ID, asset.Location.ID);
            Assert.AreEqual(asset2.Category.ID, asset.Category.ID);

            // Remove the assets created (so their child subobjects can also be removed by the cleanup.
            _assetRep.Remove(asset1);
            _assetRep.Remove(asset2);
            _assetRep.UnitOfWork.Commit();

        }
    }
}
